import { CopyDeepOptionsType, CommonUtil } from '../../common'
import { FunUtil } from '../../fun'
import { ArrUtil } from '../../arr'
import { ObjUtil } from '../../obj'

/**
 * 深拷贝数组
 *
 * @param {any[]} arr 需要进行拷贝的数组
 * @param {CopyDeepOptionsType} options 深拷贝数组的配置选项：
 * - props 指定对象类型数据中需要拷贝的属性名称数组，如果不指定，即 props 数组的长度为 0，则
 * 拷贝对象类型数据所有属性，如果在 props 中指定了要拷贝的属性，即 props 的长度大于 0，则不在
 * props 中的属性将不会被拷贝，默认值 `[]`
 * - excludeProps 指定对象类型数据中需要排除拷贝的属性名称数组，如果不指定，即 props 数组的
 * 长度为 0，则不排除任何属性，如果相同的属性名同时出现在 props 和 excludeProps 中，excludeProps
 * 优先级更高，默认值 `[]`
 * - isCopyObjUndefinedProp 对于对象类型数据，原始对象中属性值为 undefined 的属性是否对其
 * 进行拷贝，默认值为 `true`
 * - isCopyObjNullProp 对于对象类型数据，原始对象中属性值为 null 的属性是否对其进行拷贝，默
 * 认值为 `true`
 * - isCopyObjEmptyArrProp 对于数组中对象类型数据，原始对象中属性值为空数组的属性是否进行拷
 * 贝，默认值为 true
 * - propsMap 对于对象类型数据，原始对象中的属性与拷贝目标对象属性之间的映射关系，即原始对象中
 * 指定属性的属性值要拷贝到目标对象中的哪些属性上，如果要从原始对象拷贝到目标对象上的属性没有指
 * 定映射关系，则使用原始对象中的属性名作为目标对象上的属性名，默认值 `{}`
 * - targetPropValPreHandler 对于对象类型数据，原始对象属性的属性值拷贝到目标对象目标属性时
 * 对目标对象目标属性属性值的预处理函数，该函数接收：
 *   - 目前正在拷贝的原始对象
 *   - 目前正在拷贝的原始对象属性递归处理完成的属性值
 *   - 目前正在拷贝的原始对象属性的属性名
 *   - 要拷贝到目标对象目标属性的属性名
 * 作为参数，该函数需要返回要赋值给目标对象目标属性的属性值，默认值 `{}`
 * - excludeArrItem 数组中元素的排除条件，<b style="color:red">在对数组类型的数据进行拷贝
 * 时，该条件的判断在所有判断之前执行</b>，该函数接收当前遍历到的数组元素作为参数，返回 true
 * 则表示不对该元素进行拷贝，返回 false 则不排除该元素的拷贝，默认值 `() => false`
 * - isCopyArrUndefinedItem 对于数组中值为 undefined 的元素，是否对其进行拷贝，默认值 `true`
 * - isCopyArrNullItem 对于数组中值为 null 的元素，是否对其进行拷贝，默认值 `true`
 * - isCopyArrEmptyArrItem 对于数组中值为空数组的元素，是否进行拷贝，默认值为 `true`
 * - sort 用于指定数组类型数据拷贝后的目标数组中元素排序的函数，该函数接收两个数组元素作为参数，
 * 该函数需要返回一个数字类型的数据，如果返回的为负数，a 排在 b 前面；如果返回 0，a 和 b 的相
 * 对位置不变；如果返回正数，a 排在 b 后面。当 sort 没有指定时，表示不对数组进行排序，默认值
 * `undefined`
 * @returns {any[]} 深拷贝后的结果数组
 */
export const copyDeep = (arr: any[], options: CopyDeepOptionsType = {}): any[] => {
  const {
    props,
    excludeProps,
    isCopyObjUndefinedProp,
    isCopyObjNullProp,
    isCopyObjEmptyArrProp,
    propsMap,
    targetPropValPreHandler,
    excludeArrItem,
    isCopyArrUndefinedItem,
    isCopyArrNullItem,
    isCopyArrEmptyArrItem,
    sort
  } = {
    props: [],
    excludeProps: [],
    isCopyObjUndefinedProp: true,
    isCopyObjNullProp: true,
    isCopyObjEmptyArrProp: true,
    propsMap: {},
    targetPropValPreHandler: {},
    excludeArrItem: () => false,
    isCopyArrUndefinedItem: true,
    isCopyArrNullItem: true,
    isCopyArrEmptyArrItem: true,
    sort: undefined,
    ...options
  }
  const target: any[] = []
  // 遍历原始数组
  for (const item of arr) {
    // 根据指定的数组中元素的排除条件进行判断是否跳过当前元素的拷贝
    if (excludeArrItem(item)) continue
    // 如果当前遍历的元素的类型为 undefined
    if (CommonUtil.isUndefined(item)) {
      // 如果配置了不对数组中的 undefined 元素进行拷贝，则跳过当前元素的拷贝
      if (!isCopyArrUndefinedItem) continue
      target.push(item)
    }
    // 如果当前遍历的元素的类型为 null
    else if (CommonUtil.isNull(item)) {
      // 如果配置了不对数组中的 null 元素进行拷贝，则跳过当前元素的拷贝
      if (!isCopyArrNullItem) continue
      target.push(item)
    }
    // 如果当前遍历的元素的类型为 function, 则直接赋值
    else if (FunUtil.isFun(item)) target.push(item)
    // 如果当前遍历的元素的类型为数组, 则递归进行深拷贝
    else if (ArrUtil.isArr(item)) {
      // 如果当前遍历的元素值为空数组
      if (ArrUtil.isEmpty(item)) {
        // 如果配置了不对数组中的空数组元素进行拷贝，则跳过当前元素的拷贝
        if (!isCopyArrEmptyArrItem) continue
        target.push(item)
      } else {
        target.push(
          copyDeep(item, {
            props,
            excludeProps,
            isCopyObjUndefinedProp,
            isCopyObjNullProp,
            isCopyObjEmptyArrProp,
            propsMap,
            targetPropValPreHandler,
            excludeArrItem,
            isCopyArrUndefinedItem,
            isCopyArrNullItem,
            isCopyArrEmptyArrItem,
            sort
          })
        )
      }
    }
    // 如果当前遍历的元素的类型为对象, 则使用深度拷贝对象方法进行深拷贝
    else if (ObjUtil.isObj(item)) {
      target.push(
        ObjUtil.copyDeep(item, {
          props,
          excludeProps,
          isCopyObjUndefinedProp,
          isCopyObjNullProp,
          isCopyObjEmptyArrProp,
          propsMap,
          targetPropValPreHandler,
          excludeArrItem,
          isCopyArrUndefinedItem,
          isCopyArrNullItem,
          isCopyArrEmptyArrItem,
          sort
        })
      )
    }
    // 其他情况直接赋值
    else {
      target.push(item)
    }
  }
  // 如果需要对拷贝结果数组进行排序
  if (sort) {
    target.sort(sort)
  }
  return target
}

export type CopyDeepType = typeof copyDeep
